/*
 * cc.h
 *
 * Compare/Capture Unit(Smart PWM), using timer2's CC0~CC3.
 *
 * Copyright (C) 2022 Microwell, Inc.
 * Subject to the GNU Public License, version 2.
 *
 * Author: guoming<guoming@microwell.net>
 */
#ifndef __CC_H__
#define __CC_H__
#include "core.h"

#define CONFIF_SMPWM_VARIABLE_FREQ           (0)

#define SMPWM_MAX_DUTY_VAL                   (SYS_SCLK_FREQ / CONFIG_TIMER2_TCLKDIV / CONFIG_TIMER2_FREQ)
#define TIMER2_DUTY_RELOAD(DUTY)             (TIMER2_MAX_VAL - DUTY)
/**
 * @brief duty reload
 * MAX_VAL is timer max value
 * Duty cycle ranges from 0 to 100
 */
#define TIMER2_DUTY_VAL(FREQ, DUTY)          (TIMER2_MAX_VAL - 1 - (100 - DUTY) * (SYS_SCLK_FREQ / CONFIG_TIMER2_TCLKDIV / FREQ) / 100)

/*
 * duty: 0 ~ 100 duty
 */
#define CC0_DUTY_PER_SET(DUTY)               do{ WRITE_REG(TCC0L, (TIMER2_DUTY_VAL(CONFIG_TIMER2_FREQ, DUTY) & 0xff)); MODIFY_REG(TCC0H, 0x1F, ((TIMER2_DUTY_VAL(CONFIG_TIMER2_FREQ, DUTY) >> 8) & 0x1F)); } while (0)
#define CC1_DUTY_PER_SET(DUTY)               do{ WRITE_REG(TCC1L, (TIMER2_DUTY_VAL(CONFIG_TIMER2_FREQ, DUTY) & 0xff)); MODIFY_REG(TCC1H, 0x1F, ((TIMER2_DUTY_VAL(CONFIG_TIMER2_FREQ, DUTY) >> 8) & 0x1F)); } while (0)
#define CC2_DUTY_PER_SET(DUTY)               do{ WRITE_REG(TCC2L, (TIMER2_DUTY_VAL(CONFIG_TIMER2_FREQ, DUTY) & 0xff)); MODIFY_REG(TCC2H, 0x1F, ((TIMER2_DUTY_VAL(CONFIG_TIMER2_FREQ, DUTY) >> 8) & 0x1F)); } while (0)
#define CC3_DUTY_PER_SET(DUTY)               do{ WRITE_REG(TCC3L, (TIMER2_DUTY_VAL(CONFIG_TIMER2_FREQ, DUTY) & 0xff)); MODIFY_REG(TCC3H, 0x1F, ((TIMER2_DUTY_VAL(CONFIG_TIMER2_FREQ, DUTY) >> 8) & 0x1F)); } while (0)
/*
 * duty: 0 ~ max duty(please get from smart_pwm_get_max_duty,
 * thus, the real duty equal to "duty / (SMPWM_MAX_DUTY + 1)"
 */
#define CC0_DUTY_SET(DUTY)                   do{ WRITE_REG(TCC0L, (TIMER2_DUTY_RELOAD(DUTY) & 0xff)); MODIFY_REG(TCC0H, 0x1F, ((TIMER2_DUTY_RELOAD(DUTY) >> 8) & 0x1F)); } while (0)
#define CC1_DUTY_SET(DUTY)                   do{ WRITE_REG(TCC1L, (TIMER2_DUTY_RELOAD(DUTY) & 0xff)); MODIFY_REG(TCC1H, 0x1F, ((TIMER2_DUTY_RELOAD(DUTY) >> 8) & 0x1F)); } while (0)
#define CC2_DUTY_SET(DUTY)                   do{ WRITE_REG(TCC2L, (TIMER2_DUTY_RELOAD(DUTY) & 0xff)); MODIFY_REG(TCC2H, 0x1F, ((TIMER2_DUTY_RELOAD(DUTY) >> 8) & 0x1F)); } while (0)
#define CC3_DUTY_SET(DUTY)                   do{ WRITE_REG(TCC3L, (TIMER2_DUTY_RELOAD(DUTY) & 0xff)); MODIFY_REG(TCC3H, 0x1F, ((TIMER2_DUTY_RELOAD(DUTY) >> 8) & 0x1F)); } while (0)
/**
 * @brief please refer to T2CON_REG
 * CC0 = TCCS0_BIT; CC1 = TCCS1_BIT; CC1 = TCCS1_BIT; CC0 = TCCS1_BIT; 
 * LEVEL polarity: 0 normal, high active; 1 reversed, low active
 */
#define CC0_POLARITY_SET(LEVEL)              MODIFY_REG(T2CON, TCCS0_BIT, (LEVEL << 0))
#define CC1_POLARITY_SET(LEVEL)              MODIFY_REG(T2CON, TCCS1_BIT, (LEVEL << 1))
#define CC2_POLARITY_SET(LEVEL)              MODIFY_REG(T2CON, TCCS2_BIT, (LEVEL << 2))
#define CC3_POLARITY_SET(LEVEL)              MODIFY_REG(T2CON, TCCS3_BIT, (LEVEL << 3))

#define CC_POLARITY_SET(CC, LEVEL)           MODIFY_REG(T2CON, CC, (LEVEL << 0))
/**
 * @brief CC mode selector, compare enable(10)
 * 
 */
#define CC0_MODE_COMPARE_SET(EN)             MODIFY_REG(T2MOD, 0x03, (TCCM0_CO & (EN << 1)))
#define CC1_MODE_COMPARE_SET(EN)             MODIFY_REG(T2MOD, 0x0C, (TCCM1_CO & (EN << 3)))
#define CC2_MODE_COMPARE_SET(EN)             MODIFY_REG(T2MOD, 0x30, (TCCM2_CO & (EN << 5)))
#define CC3_MODE_COMPARE_SET(EN)             MODIFY_REG(T2MOD, 0xC0, (TCCM3_CO & (EN << 7)))

/**
 * @brief CC mode selector, capture enable(01)
 * 
 */
#define CC0_MODE_CAPTURE_SET(EN)             MODIFY_REG(T2MOD, 0x03, (TCCM0_CO & (EN << 0)))
#define CC1_MODE_CAPTURE_SET(EN)             MODIFY_REG(T2MOD, 0x0C, (TCCM1_CO & (EN << 2)))
#define CC2_MODE_CAPTURE_SET(EN)             MODIFY_REG(T2MOD, 0x30, (TCCM2_CO & (EN << 4)))
#define CC3_MODE_CAPTURE_SET(EN)             MODIFY_REG(T2MOD, 0xC0, (TCCM3_CO & (EN << 6)))

/**
 * @brief SBLEDx(0-3) is CCx(0-3)
 * Used to extract information about sbled_info
 */
#if (CC_CHANNEL_MODE)
typedef enum {
#if (CC_CHANNEL_MODE & CC0)
    BELD0,   // for CC0
#endif
#if (CC_CHANNEL_MODE & CC1)
    BELD1,   // for CC1
#endif
#if (CC_CHANNEL_MODE & CC2)
    BELD2,   // for CC2
#endif
#if (CC_CHANNEL_MODE & CC3)
    BELD3,   // for CC3
#endif
} BLED_SORT;
#endif

#if (CC_CHANNEL_MODE)
void cc_init(void);
void cc_set_polarity(uint8_t cc, uint8_t polarity);
void cc_set_duty(uint8_t cc, uint16_t duty);
void cc_exit(void);
void cc_io_enable(uint8_t cc);
void cc_io_disable(uint8_t cc);
void cc_set_freq(uint32_t freq);
uint16_t cc_get_max_duty(void);
#else
#define cc_init();
#define cc_set_polarity(cc, polarity);
#define cc_set_duty(cc, duty);
#define cc_exit();
#define cc_io_enable(cc);
#define cc_io_disable(cc);
#define cc_set_freq(freq);
#define cc_get_max_duty()      (0)
#endif
#endif